﻿using AutoMapper;
using CucumberJwt.Application.Authorization.Audit.Dto;
using CucumberJwt.Application.Authorization.Audit.Service;
using EntityFrameworkCore;
using EntityFrameworkCore.Model;
using FrameworkCore.Dependency;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Claims;
using System.Threading.Tasks;
using UAParser;
using WebApi.CustomAttribute;

namespace WebApi.Filter
{
    public class AuditActionFilter : IAsyncActionFilter
    {
        private IHttpContextAccessor httpContextAccessor;
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {

            var disableAuditingAttribute = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo.GetCustomAttributes<DisableAuditingAttribute>()?.FirstOrDefault();
            if (!(context.ActionDescriptor is ControllerActionDescriptor) || disableAuditingAttribute != null)
            {
                await next();
                return;
            }
            httpContextAccessor = ServiceLocator.Instance.GetService<IHttpContextAccessor>();
            var controllerAction = context.ActionDescriptor as ControllerActionDescriptor;
            var auditInfo = this.CreateAuditInfo(controllerAction.ControllerTypeInfo.AsType(), controllerAction.MethodInfo, context.ActionArguments);
            auditInfo.ClientIPAddress = this.GetClientIpAddress();
            auditInfo.ClientName = this.GetUserName();
            auditInfo.BrowserInfo = this.GetBrowserInfo();
            var stopwatch = Stopwatch.StartNew();
            try
            {
                var result = await next();
                if (result.Exception != null && !result.ExceptionHandled)
                {
                    auditInfo.Exception = result.Exception;
                }
            }
            catch (Exception ex)
            {
                auditInfo.Exception = ex;
                throw;
            }
            finally
            {
                stopwatch.Stop();
                auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
                Save(auditInfo);

            }

        }

        public string GetUserName()
        {
            var username = System.Net.Dns.GetHostEntry(httpContextAccessor.HttpContext.Connection.RemoteIpAddress).HostName;
            return username;
        }
        private string GetBrowserInfo()
        {
            var userAgent = httpContextAccessor.HttpContext.Request.Headers[HeaderNames.UserAgent];
            if (userAgent.ToString() == null)
            {
                return null;
            }
            string uaString = Convert.ToString(userAgent[0]);
            var uaParser = Parser.GetDefault();
            ClientInfo c = uaParser?.Parse(uaString);
            return c?.ToString() ?? string.Empty;

        }

        private string GetClientIpAddress()
        {
            var httpContext = httpContextAccessor.HttpContext;
            if (httpContext?.Connection.RemoteIpAddress == null)
            {
                return null;
            }

            var clientIp = httpContext.Connection.RemoteIpAddress.ToString();

            try
            {
                foreach (var hostAddress in Dns.GetHostAddresses(clientIp))
                {
                    if (hostAddress.AddressFamily == AddressFamily.InterNetwork)
                    {
                        return hostAddress.ToString();
                    }
                }

                foreach (var hostAddress in Dns.GetHostAddresses(Dns.GetHostName()))
                {
                    if (hostAddress.AddressFamily == AddressFamily.InterNetwork)
                    {
                        return hostAddress.ToString();
                    }
                }
            }
            catch (Exception ex)
            {

            }

            return clientIp;
        }

        private void Save(AuditDto auditInfo)
        {

            //var userDto = auditInfo.MapTo<SysUser>();
            var auditLog = Mapper.Map<AuditDto, AuditLog>(auditInfo);
            auditLog.Id = Guid.NewGuid();
            var auditService = ServiceLocator.Instance.GetService<IAuditService>();
            auditService.Add(auditLog);
            //using (AuditDbContext dbContext = new AuditDbContext())
            //{

            //    try
            //    {
            //        dbContext.AuditLogss.Add(
            //      new AuditLogs()
            //        {

            //            Id = Guid.NewGuid(),
            //            //UserId = auditInfo.UserId??null,
            //            ServiceName = auditInfo.ServiceName,
            //            MethodName = auditInfo.MethodName,
            //            Parameters = auditInfo.Parameters,
            //            ExecutionTime = auditInfo.ExecutionTime,
            //            ExecutionDuration = auditInfo.ExecutionDuration,
            //            ClientIPAddress = auditInfo.ClientIPAddress,
            //            ClientName = auditInfo.ClientName,
            //            BrowserInfo = auditInfo.BrowserInfo

            //        });
            //        dbContext.SaveChanges();
            //    }
            //    catch (Exception ex)
            //    {

            //        throw ex;
            //    }
            //}
        }
        private AuditDto CreateAuditInfo(Type type, MethodInfo method, IDictionary<string, object> arguments)
        {
            var userid = httpContextAccessor.HttpContext.User?.Claims.FirstOrDefault(o => o.Type == ClaimTypes.NameIdentifier)?.Value;
            var auditDto = new AuditDto()
            {

                UserId = string.IsNullOrEmpty(userid) ? default(Guid?) : Guid.Parse(userid),
                ServiceName = type != null ? type.FullName : string.Empty,
                MethodName = method.Name,
                Parameters = ConvertArgumentsToJson(arguments),
                ExecutionTime = DateTime.Now
            };
            return auditDto;
        }

        private string ConvertArgumentsToJson(IDictionary<string, object> arguments)
        {
            try
            {
                if (arguments == null || arguments.Count == 0)
                {
                    return "{}";
                }


                return JsonConvert.SerializeObject(arguments);
            }
            catch (Exception ex)
            {

                return "{}";
            }
        }
    }
}
